﻿using Helpers;
using Helpers.Mongo;
using Helpers.Repository;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Options;
using MongoDB.Driver;
using Serilog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Text.Json;
using System.Runtime.InteropServices;
using System.Text.Unicode;
using System.Text.Encodings.Web;
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection.Extensions;

/// <summary>
/// mongoHelper扩展方法
/// </summary>
public static class MongoExtensions
{
    public static List<Type> _mongoRepositoryTypes { get; } = new List<Type>();
    /// <summary>
    /// 注入mongo的配置,客户端,默认数据库,仓储类
    /// </summary>
    /// <param name="services">服务</param>
    /// <param name="configuration">配置</param>
    /// <param name="configure"></param>
    public static IServiceCollection AddMongoDbz(this IServiceCollection services, IConfiguration configuration, Action<MongoOptions> configure = null)
    {
        //增加DateTimeOffset序列化
        BsonSerializer.RegisterSerializer<DateTimeOffset>(new DateTimeOffsetSupportingBsonDateTimeSerializer());

        //指定Dictionary存储方式
        ConventionRegistry.Register("DictionaryRepresentationConvention", new ConventionPack { new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays) }, _ => true);

        //注入mongo配置
        services.AddOptions<MongoOptions>()
            .Bind(configuration.GetSection(MongoOptions.SectionName))
            .ValidateDataAnnotations();
        services.PostConfigure<MongoOptions>(x =>
        {
            //配置BsonChunkPool 
            if (x.BsonChunkPool != null)
            {
                BsonChunkPool.Default = x.BsonChunkPool;
            }
            configure?.Invoke(x);
        });
        services.TryAddSingleton<MongoOptions>(x => x.GetRequiredService<IOptions<MongoOptions>>().Value);

        //注入mongo客户端
        services.TryAddSingleton<IMongoClient>(x => new MongoClient(x.GetRequiredService<MongoOptions>().Connection));

        //注入mongo默认数据库
        services.TryAddSingleton<IMongoDatabase>(x => x.GetRequiredService<IMongoClient>().GetDatabase(x.GetRequiredService<MongoOptions>().DatabaseName));

        //自动创建和删除索引
        services.AddSingleton<IStartupTask, MongoIndexStartupTask>();

        //注入mongo仓储类
        AddRepository();

        return services;

        void AddRepository()
        {
            var config = configuration.GetSection(MongoOptions.SectionName).Get<MongoOptions>();
            if (config != null && configure != null) configure(config);
            Console.WriteLine($"mongo配置--{JsonSerializer.Serialize(config, new JsonSerializerOptions() { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })}");

            if (config?.RepositoryAssemblyNames?.Count > 0)
            {
                foreach (var RepositoryAssemblyName in config.RepositoryAssemblyNames)
                {
                    try
                    {
                        if (!string.IsNullOrEmpty(RepositoryAssemblyName))
                        {
                            var allTypes = Assembly.Load(new AssemblyName(RepositoryAssemblyName)).GetTypes()
                                .Where(x => x.IsClass && typeof(IMongoRepository).IsAssignableFrom(x));
                            Console.WriteLine($"{RepositoryAssemblyName} 发现注册 {allTypes.Count()}个");
                            AddRepositoryOnType(allTypes);
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("注册mongo仓储异常:" + ex.ToString());
                    }
                }
            }
            else
            {
                AddRepositoryOnType(DiHelper.GetTypes(new[] { typeof(IMongoRepository) }));
            }
        }

        int AddRepositoryOnType(IEnumerable<Type> types)
        {
            if (types?.Count() > 0)
            {
                foreach (var item in types)
                {
                    _mongoRepositoryTypes.Add(item);
                    //if (typeof(ITransientMongoRepository).IsAssignableFrom(item))
                    //{
                    //    services.AddTransient(item);
                    //}
                    //else if (typeof(IScopedMongoRepository).IsAssignableFrom(item))
                    //{
                    //    services.AddScoped(item);
                    //}
                    //else if (typeof(ISingletonMongoRepository).IsAssignableFrom(item))
                    //{
                    //    services.AddSingleton(item);
                    //}
                    //else
                    //{
                    //    throw new Exception($"不支持的类型{item.Name}");
                    //}
                    services.AddSingleton(item);
                }
                Console.WriteLine($"mongo仓储--{string.Join(",", types.Select(x => x.Name))}");
            }
            return types?.Count() ?? 0;
        }
    }

    public static IMongoCollection<T> GetCollection<T>(this IMongoDatabase mongoDatabase, MongoCollectionSettings settings = null)
    {
        return mongoDatabase.GetCollection<T>(typeof(T).Name, settings);
    }

    public static T Deserialize<T>(this BsonDocument document)
    {
        return BsonSerializer.Deserialize<T>(document);
    }

    public static List<T> DeserializeList<T>(this List<BsonDocument> documents)
    {
        return documents?.Select(x => x.Deserialize<T>()).ToList();
    }

    public static FilterDefinition<T> And<T>(this FilterDefinition<T> filter, Expression<Func<T, bool>> expression)
    {
        filter &= Builders<T>.Filter.Where(expression);
        return filter;
    }
    public static FilterDefinition<T> AndIf<T>(this FilterDefinition<T> filter, Func<bool> condition, Expression<Func<T, bool>> expression)
    {
        if (condition())
        {
            filter &= Builders<T>.Filter.Where(expression);
        }
        return filter;
    }

    public static FilterDefinition<T> Or<T>(this FilterDefinition<T> filter, Expression<Func<T, bool>> expression)
    {
        filter |= Builders<T>.Filter.Where(expression);
        return filter;
    }

    public static FilterDefinition<T> OrIf<T>(this FilterDefinition<T> filter, Func<bool> condition, Expression<Func<T, bool>> expression)
    {
        if (condition())
        {
            filter |= Builders<T>.Filter.Where(expression);
        }
        return filter;
    }
}
